﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Globalization;

using hentity;
using hlog;

namespace Dapper {
    static partial class SqlMapper {
        static int logTime = 500;
        private static T ExecuteScalarImpl<T>(IDbConnection cnn,ref CommandDefinition command) {
            long begin = DateTime.Now.Ticks;
            lock (cnn) {
                Action<IDbCommand,object> paramReader = null;
                object param = command.Parameters;
                if (param != null) {
                    var identity = new Identity(command.CommandText,command.CommandType,cnn,null,param.GetType(),null);
                    paramReader = GetCacheInfo(identity,command.Parameters,command.AddToCache).ParamReader;
                }
                IDbCommand cmd = null;
                bool wasClosed = cnn.State == ConnectionState.Closed;
                object result;
                try {
                    cmd = command.SetupCommand(cnn,paramReader);
                    if (wasClosed)
                        cnn.Open();
                    result = cmd.ExecuteScalar();
                    command.OnCompleted();
                } finally {
                    if (wasClosed)
                        cnn.Close();
                    if (cmd != null)
                        cmd.Dispose();
                }
                int between =(int) (DateTime.Now.Ticks - begin)/10000;
                if( between > logTime ) {
                    LoggerCommon.fileAll("dpbExec" + command.CommandText+ between);
                }
                 return Parse<T>(result);
            }
        }
        private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn,CommandDefinition command,Type effectiveType) {//查询接口
            long begin = DateTime.Now.Ticks;
            lock (cnn) {
                object param = command.Parameters;
                var identity = new Identity(command.CommandText,command.CommandType,cnn,effectiveType,param == null ? null : param.GetType(),null);
                var info = GetCacheInfo(identity,param,command.AddToCache);
                IDbCommand cmd = null;
                IDataReader reader = null;
                bool wasClosed = cnn.State == ConnectionState.Closed;
                try {
                    cmd = command.SetupCommand(cnn,info.ParamReader);
                    if (wasClosed)
                        cnn.Open();
                    reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess);
                    wasClosed = false;
                    // *if* the connection was closed and we got this far, then we now have a reader
                    // with the CloseConnection flag, so the reader will deal with the connection; we
                    // still need something in the "finally" to ensure that broken SQL still results
                    // in the connection closing itself
                    var tuple = info.Deserializer;
                    int hash = GetColumnHash(reader);
                    if (tuple.Func == null || tuple.Hash != hash) {
                        if (reader.FieldCount == 0)
                            yield break;
                        tuple = info.Deserializer = new DeserializerState(hash,GetDeserializer(effectiveType,reader,0,-1,false));
                        if (command.AddToCache)
                            SetQueryCache(identity,info);
                    }
                    var func = tuple.Func;
                    var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
                    while (reader.Read()) {
                        object val = func(reader);

                        if( val == null   ) {
                            yield return default(T);
                        }else if (val == null || val is T) {
                            yield return (T) val;
                        } else {
                            yield return (T)Convert.ChangeType(val,convertToType,CultureInfo.InvariantCulture);
                        }
                    }
                    while (reader.NextResult()) {
                    }
                    // happy path; close the reader cleanly - no
                    // need for "Cancel" etc
                    reader.Dispose();
                    reader = null;
                    command.OnCompleted();
                } finally {
                    if (reader != null) {
                        if (!reader.IsClosed)
                            try {
                                cmd.Cancel();
                            } catch { /* don't spoil the existing exception */ }
                        reader.Dispose();
                    }
                    if (wasClosed)
                        cnn.Close();
                    if (cmd != null)
                        cmd.Dispose();
                }
                int between = ( int ) (DateTime.Now.Ticks - begin) / 10000;
                if( between > logTime ) {
                    LoggerCommon.fileAll("dpbQuery" + command.CommandText + between);
                }
            }
        }
        public static Page<T> QueryMultiple<T>(//分页查询
         this IDbConnection cnn,string sql,object param1 = null,IDbTransaction transaction = null,int? commandTimeout = null,CommandType? commandType = null
) {
            long begin = DateTime.Now.Ticks;
            var command = new CommandDefinition(sql,(object)param1,transaction,commandTimeout,commandType,CommandFlags.Buffered);
            lock (cnn) {
                object param = command.Parameters;
                Identity identity = new Identity(command.CommandText,command.CommandType,cnn,typeof(GridReader),param == null ? null : param.GetType(),null);
                CacheInfo info = GetCacheInfo(identity,param,command.AddToCache);
                IDbCommand cmd = null;
                IDataReader reader = null;
                bool wasClosed = cnn.State == ConnectionState.Closed;
                try {
                    if (wasClosed)
                        cnn.Open();
                    cmd = command.SetupCommand(cnn,info.ParamReader);
                    reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess);
                    var ds = new GridReader(cmd,reader,identity,command.Parameters as DynamicParameters,command.AddToCache);
                    cmd = null; // now owned by result
                    List<T> it = ds.Read<T>().ToList();
                    object c = ds.Read<int>().First();
                    int count = int.Parse(c.ToString());
                    ds.Dispose();
                    ds = null;
                    Page<T> page = new Page<T>();
                    page.totalSize = count;
                    page.content = it;
                      int between = ( int ) (DateTime.Now.Ticks - begin) / 10000;
                    if( between > logTime ) {
                        LoggerCommon.fileAll("dpbMultQuery" + command.CommandText + between);
                    }
                     return page;
                } catch (Exception e){
                    if (reader != null) {
                        if (!reader.IsClosed)
                            try {
                                if( cmd != null ) {
                                    cmd.Cancel();
                                }
                              
                            } catch { /* don't spoil the existing exception */ }
                        reader.Dispose();
                    }
                    if (cmd != null)
                        cmd.Dispose();
                    if (wasClosed)
                        cnn.Close();
                    throw e;
                }
            }
        }
        private static int ExecuteImpl(this IDbConnection cnn,ref CommandDefinition command) {
             lock (cnn) {
                object param = command.Parameters;
                IEnumerable multiExec = GetMultiExec(param);
                if (multiExec != null) {
                   return ExecuteCommand2(cnn ,ref command  ,multiExec);
                }
                CacheInfo info = null;
                // nice and simple
                if (param != null) {
                    Identity identity = new Identity(command.CommandText,command.CommandType,cnn,null,param.GetType(),null);
                    info = GetCacheInfo(identity,param,command.AddToCache);
                }
                return ExecuteCommand(cnn,ref command,param == null ? null : info.ParamReader);
            }
        }
         private static int ExecuteCommand2(IDbConnection cnn ,ref CommandDefinition command ,IEnumerable multiExec) {
            bool isFirst = true;
            int total = 0;
            bool wasClosed = cnn.State == ConnectionState.Closed;
            CacheInfo info = null;
        
            try {
                if( wasClosed )
                    cnn.Open();
                using( var cmd = command.SetupCommand(cnn ,null) ) {
                    string masterSql = null;
                    foreach( var obj in multiExec ) {
                        if( isFirst ) {
                            masterSql = cmd.CommandText;
                            isFirst = false;
                            Identity identity = new Identity(command.CommandText ,cmd.CommandType ,cnn ,null ,obj.GetType() ,null);
                            info = GetCacheInfo(identity ,obj ,command.AddToCache);
                        } else {
                            cmd.CommandText = masterSql; // because we do magic replaces on "in" etc
                            cmd.Parameters.Clear(); // current code is Add-tastic
                        }
                        info.ParamReader(cmd ,obj);
                        total += cmd.ExecuteNonQuery();
                    }
                }
                command.OnCompleted();
            } finally {
                if( wasClosed )
                    cnn.Close();
            }
            return total;
        }
          private static int ExecuteCommand(IDbConnection cnn,ref CommandDefinition command,Action<IDbCommand,object> paramReader) {
            long begin = DateTime.Now.Ticks;
            IDbCommand cmd = null;
            bool wasClosed = cnn.State == ConnectionState.Closed;
            try {
                cmd = command.SetupCommand(cnn,paramReader);
                if (wasClosed)
                    cnn.Open();
                int result = cmd.ExecuteNonQuery();
                command.OnCompleted();
                int between = ( int ) (DateTime.Now.Ticks - begin) / 10000;
                if( between > logTime ) {
                    LoggerCommon.fileAll("dpbNoQuery" + command.CommandText + between);
                }
                return result;
            } finally {
                if (wasClosed)
                    cnn.Close();
                if (cmd != null)
                    cmd.Dispose();
            }
        }
    }
}
